#include "gofgame/common/graphics/HornedTextureBrush.h"
#include "gofgame/common/graphics/SolidBrush.h"
#include "gofgame/common/graphics/LinearGradientBrush.h"
#include "gofgame/common/IllegalArgumentException.h"

using namespace gofgame::common::graphics;

static void mixColor(float pos, float len, const ccColor4F &start, const ccColor4F &end, ccColor4F &output) {
	output.r = start.r + (end.r - start.r) * pos / len;
	output.g = start.g + (end.g - start.g) * pos / len;
	output.b = start.b + (end.b - start.b) * pos / len;
	output.a = start.a + (end.a - start.a) * pos / len;
}

struct Vertex {
	GLfloat x;
	GLfloat y;
};

struct Coordinate {
	GLfloat u;
	GLfloat v;
};

HornedTextureBrush::HornedTextureBrush(const CCSize &cornerSize, CCTexture2D *texture, Brush *baseBrush) 
	: TextureBrush(texture, baseBrush), cornerSize(cornerSize) {
	if (cornerSize.width <= 0 || cornerSize.height <= 0) {
		_throw(IllegalArgumentException, "both the width and height of cornerSize must be greater than zero");
	}
}

const CCSize &HornedTextureBrush::getCornerSize() const {
	return this->cornerSize;
}

void HornedTextureBrush::onFillRect(float left, float top, float width, float height) {
	CCSize texSize = this->getTexture()->getContentSize();
	float cornerWidth = _min(this->cornerSize.width, _min(texSize.width, width) / 2);
	float cornerHeight = _min(this->cornerSize.height, _min(texSize.height, height) / 2);
	Vertex vertexArr[24];
	ccColor4F colorArr[24];
	Coordinate coordinateArr[24];

	float xArr[] = { 0, cornerWidth, width - cornerWidth, width };
	float yArr[] = { 0 , cornerHeight, height - cornerHeight, height };
	float uArr[] = { 0, cornerWidth / texSize.width, (texSize.width - cornerWidth) / texSize.width, 1 };
	float vArr[] = { 1, (texSize.height - cornerHeight) / texSize.height, cornerHeight / texSize.height, 0 };
	for (int i = 0; i < 3; i++) {
		Vertex *pv = vertexArr + 8 * i;
		Coordinate *pc = coordinateArr + 8 * i;
		for (int ii = 0; ii < 4; ii++) {
			pv[ii << 1].x = left + xArr[ii];
			pv[ii << 1].y = top + yArr[i];
			pv[(ii << 1) + 1].x = left + xArr[ii];
			pv[(ii << 1) + 1].y = top + yArr[i + 1]; 
			pc[ii << 1].u = uArr[ii];
			pc[ii << 1].v = vArr[i];
			pc[(ii << 1) + 1].u = uArr[ii];
			pc[(ii << 1) + 1].v = vArr[i + 1]; 
		}
	}
	
	Brush *baseBrush = this->getBaseBrush();
	if (dynamic_cast<SolidBrush*>(baseBrush)) {
		SolidBrush *solidBrush = static_cast<SolidBrush*>(baseBrush);
		ccColor4F color = solidBrush->getColor();
		for (int i = 0; i < 24; i++) {
			colorArr[i] = color;
		}
	} else {
		LinearGradientBrush *linearGradientBrush = static_cast<LinearGradientBrush*>(baseBrush);
		ccColor4F leftTopColor;
		ccColor4F leftBottomColor;
		ccColor4F rightTopColor;
		ccColor4F rightBottomColor;
		if (linearGradientBrush->getLinearGradientDirection() == LinearGradientDirection_Vertical) {
			leftTopColor = rightTopColor = linearGradientBrush->getStartColor();
			leftBottomColor = rightBottomColor = linearGradientBrush->getEndColor();
		} else {
			leftTopColor = leftBottomColor = linearGradientBrush->getStartColor();
			rightTopColor = rightBottomColor = linearGradientBrush->getEndColor();
		}
		for (int i = 0; i < 3; i++) {
			ccColor4F *pc = colorArr + 8 * i;
			for (int ii = 0; ii < 4; ii++) {
				ccColor4F topColor, bottomColor;
				mixColor(xArr[ii], width, leftTopColor, rightTopColor, topColor);
				mixColor(xArr[ii], width, leftBottomColor, rightBottomColor, bottomColor);
				mixColor(yArr[i], height, bottomColor, topColor, pc[ii << 1]);
				mixColor(yArr[i + 1], height, bottomColor, topColor, pc[(ii << 1) + 1]);
			}
		}
	}
	CCGLProgram *program = CCShaderCache::sharedShaderCache()->programForKey(kCCShader_PositionTextureColor);
	program->use();
	program->setUniformsForBuiltins();
	ccGLEnableVertexAttribs( kCCVertexAttribFlag_Position | kCCVertexAttribFlag_Color | kCCVertexAttribFlag_TexCoords);
	ccGLBindTexture2D(this->getTexture()->getName());
    glVertexAttribPointer(kCCVertexAttrib_Position, 2, GL_FLOAT, GL_FALSE, 0, vertexArr);
	glVertexAttribPointer(kCCVertexAttrib_Color, 4, GL_FLOAT, GL_FALSE, 0, colorArr);
	glVertexAttribPointer(kCCVertexAttrib_TexCoords, 2, GL_FLOAT, GL_FALSE, 0, coordinateArr);
	for (int i = 0; i < 3; i++) {
		glDrawArrays(GL_TRIANGLE_STRIP, i * 8, 8);
	}
}
